package com.sway.data.core;


import com.sway.data.annotation.Column;
import com.sway.data.annotation.Operator;
import com.sway.data.annotation.Table;
import com.sway.data.exception.BuildingSqlException;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class SqlBuilder {

    private Query query;
    public static SqlBuilder getBuilder(){
        return new SqlBuilder();
    }

    public <T> String build(SqlType type,T entity){
        this.query = new Query();
        switch (type){
            case SELECT:
                return buildSelect(entity);
            case INSERT:
                return buildInsert(entity);
            case UPDATE:
                return buildUpdate(entity);
            case DELETE:
            default: return "";

        }

    }

    public <T> String batchBuild(List<T> entities){
        if (entities == null || entities.size() <= 0) {
            throw new BuildingSqlException("Check batchBuild parameter, please make sure is not empty.");
        }
        String tableName = getTableName(entities.get(0));
        List<Field> fields = getFields(entities.get(0));
        return generateBatchInsertSql(fields, entities.size(), tableName);
    }

    private String generateBatchInsertSql(List<Field> fields, int size,String tableName) {
        List<String> columnList = new ArrayList<>();
        for (Field field : fields) {
            if (field.isAnnotationPresent(Column.class)){
                Column annotation = field.getAnnotation(Column.class);
                columnList.add(annotation.value());
            }
        }
        StringBuffer buffer = new StringBuffer();
        buffer.append("INSERT INTO ").append(tableName)
                .append(" ( ").append(String.join(",",columnList)).append(" ) ")
                .append("values");
        List<String> values = new ArrayList<>();
        for (int i =0;i<fields.size();i++){
            values.add("?");
        }
        String format = String.join(",",values);
        for(int i =0;i<size;i++){
            buffer.append("(").append(format).append("),");
        }
        return buffer.deleteCharAt(buffer.length()-1).append(";").toString();
    }

//    private <T> String buildBatchInsert(T entities) {
//        getTableName(e)
//    }

    private <T> String getTableName(T entity){
        if (entity.getClass().isAnnotationPresent(Table.class)){
            return entity.getClass().getAnnotation(Table.class).value();
        }else {
            throw new BuildingSqlException("Entity must be has Table Annotation.");
        }

    }
    private <T> String buildInsert(T entity) {
        String tableName = getTableName(entity);
        StringBuffer buffer = new StringBuffer();
        List<String> valueList = new ArrayList<>();
        List<String> columnList = new ArrayList<>();
        List<Field> fields = getFields(entity);
        for (Field field : fields) {
            try {
                Object value = field.get(entity);
                if (value == null || value.equals("")){
                    continue;
                }
                if (!field.isAnnotationPresent(Column.class)){
                   continue;
                }
                Column annotation = field.getAnnotation(Column.class);

                valueList.add("'" + value + "'");
                columnList.add(annotation.value());
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
        return buffer.append("INSERT INTO ").append(tableName)
                .append(" ( ").append(String.join(",", columnList)).append(" ) ")
                .append("values( ").append(String.join(",", valueList)).append(" ) ").toString();
    }

    private <T> String buildUpdate(T entity) {

        StringBuffer buffer = new StringBuffer();

        List<Field> fields = getFields(entity);
        query.where = buildWhere(entity, fields);

        List<String> updateList = new ArrayList<>();
        for (Field field : fields) {
            try {
                Object o = field.get(entity);
                if (o == null || o == ""){
                    continue;
                }
                if (field.isAnnotationPresent(Operator.class)){
                    continue;
                }
                if (field.isAnnotationPresent(Column.class)){
                    Column column = field.getAnnotation(Column.class);
                    updateList.add(column.value() +" = "+ "'" + o +"'");
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        query.field = buffer.append("update ")
                .append(entity.getClass().getAnnotation(Table.class).value())
                .append(" set ")
                .append(String.join(", ",updateList)).toString();
        return query.build();
    }

    private <T> String buildSelect(T entity) {

        List<Field> fields = getFields(entity);
//        Object instance = newInstance(clazz);
        query.where = buildWhere(entity,fields);
        query.field = buildField(entity,fields);
        return query.build();
    }

    private <T> String buildField(T target, List<Field> fields) {
        StringBuffer buffer = new StringBuffer();
        List<String> selectField = new ArrayList<>();
        for (Field field : fields) {
            try {
                if (field.isAnnotationPresent(Column.class)){
                    Column column = field.getAnnotation(Column.class);
                    selectField.add(column.value());
                }
            } catch (Exception e) {
                throw new BuildingSqlException(e.getMessage());
            }
        }
        buffer.append("SELECT ").append(String.join(", ", selectField)).append(" from ").append(target.getClass().getAnnotation(Table.class).value());
        return buffer.toString();
    }

    private <T> String buildWhere(T target,List<Field> fields) {
//        StringBuffer buffer = new StringBuffer();
        List<String> where = new ArrayList<>();
//        buffer.append(" where 1=1");
        for (Field field : fields) {
            try {
                Object value = field.get(target);
                if (value == null || value.equals("")){
                    continue;
                }
                if (field.isAnnotationPresent(Operator.class)){
                    Operator operator = field.getAnnotation(Operator.class);
                    where.add(operator.column() + operator.name() + value);
//                    buffer.append(" and " + operator.column() + operator.name() + value);
                }
            } catch (Exception e) {
               throw new BuildingSqlException(e.getMessage());
            }
        }
        if (!where.isEmpty()){
            return " where " + String.join(" and ",where);
        }
        return "";
    }

    private <T> List<Field> getFields(T target) {
        Class clazz = target.getClass();
        Field[] fields = clazz.getDeclaredFields();
        makeAccessible(fields);
        return Arrays.asList(fields);
    }

    private void makeAccessible(Field[] fields) {
        for (Field field : fields) {
            field.setAccessible(true);
        }
    }

    private <T> T newInstance(Class<T> clazz){
        try {
            Constructor<T> constructor = clazz.getDeclaredConstructor();
            return constructor.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }


    }

    public <T> Object[] paramBuild(List<T> entities) {
        List<Object> params = new ArrayList<>();
        for (T entity : entities) {
            List<Field> fields = getFields(entity);
            for (Field field : fields) {
                try {
                    params.add(field.get(entity));
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }

        }
        return params.toArray();
    }
}
